﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/**
 * 用法：创建多个Image，每个Image都附加本组件，就可以任意两两拖动连线
 * 并可以监听连线成功的回调
 */
public class Connector : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    private static Action<Connector> onSomeConnectorStartDraw;

    public Color color = Color.white;
    public float size = 10;
    public float adsorbDistance = 50;

    public RectTransform rectTransform { get { return transform as RectTransform; } }
    public Action<Connector, Connector> onConnected;
    public RectTransform line { get; private set; }
    private Canvas canvas;
    private bool isDrawing;
    private List<Connector> otherConnectorsInScene = new List<Connector>();
    private Connector adsorbTarget;

    void Awake()
    {
        onSomeConnectorStartDraw += OnSomeConnectorStartDraw;
    }

    void Update()
    {
        if (isDrawing)
        {
            Vector2 endPos = ScreenToUGUIPoint(Input.mousePosition);
            Vector2 rootPos = LocalToRootPos(rectTransform.anchoredPosition, transform.parent);

            adsorbTarget = null;
            for (int i = 0; i < otherConnectorsInScene.Count; i++)
            {
                var other = otherConnectorsInScene[i];
                var otherPos = LocalToRootPos(other.rectTransform.anchoredPosition, other.transform.parent);
                float dis = Vector2.Distance(endPos, otherPos);
                if (dis < adsorbDistance)
                {
                    endPos = otherPos;
                    adsorbTarget = other;
                    break;
                }
            }

            line.sizeDelta = new Vector2(Vector2.Distance(endPos, rootPos), size);
            line.eulerAngles = new Vector3(0, 0, Mathf.Atan2(endPos.y - rootPos.y, endPos.x - rootPos.x) * Mathf.Rad2Deg);
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if (!isDrawing)
        {
            isDrawing = true;
            if (!line)
            {
                GameObject go = new GameObject("Line");
                Image img = go.AddComponent<Image>();
                line = img.rectTransform;
                line.sizeDelta = new Vector2(size, size);
                line.pivot = new Vector2(0, 0.5f);
                line.SetParent(transform, false);
                line.anchoredPosition = Vector2.zero;
            }
            line.GetComponent<Image>().color = color;
            otherConnectorsInScene.Clear();
            onSomeConnectorStartDraw.Invoke(this);
        }
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        isDrawing = false;
        if (adsorbTarget)
        {
            onConnected?.Invoke(this, adsorbTarget);
        }
        else
        {
            if (line)
            {
                Destroy(line.gameObject);
                line = null;
            }
        }
    }

    public void Clear()
    {
        if (line)
        {
            Destroy(line.gameObject);
            line = null;
        }
    }

    private Vector2 ScreenToUGUIPoint(Vector2 screenPos)
    {
        canvas ??= GetComponentInParent<Canvas>();
        Camera camera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, screenPos, camera, out Vector2 uiPos);
        return uiPos;
    }

    private Vector2 LocalToRootPos(Vector2 uiPos, Transform parent)
    {
        CanvasScaler _canvasScaler = parent.GetComponent<CanvasScaler>();
        while (!_canvasScaler)
        {
            uiPos += (parent as RectTransform).anchoredPosition;
            parent = parent.parent as RectTransform;
            _canvasScaler = parent.GetComponent<CanvasScaler>();
        }
        return uiPos;
    }

    private void OnSomeConnectorStartDraw(Connector connector)
    {
        if (connector != this)
        {
            connector.otherConnectorsInScene.Add(this);
        }
    }

    private void OnDestroy()
    {
        onSomeConnectorStartDraw -= OnSomeConnectorStartDraw;
    }
}
